Skip to content
created by Aha00aAha00a at 2026-09-20
last modified by Aha00aAha00a at 2026-09-20
revision: 4

Dev SchemaOrgVocabulary

Dev

public/schema.org/<버전>/ 아래 파일들은 schema.org 가 배포하는 것이 아니다. 가공을 거친 것이고, 그 가공이 InterpreterSchema 와 SchemaOrg.scala 가 읽는 모양을 만든다. 버전을 올리려면 같은 가공을 다시 해야 한다.

1. 무엇이 실려 있나

파일

읽는 곳

쓰임

schemaorg-current-https.jsonld

CalculatedSchemaOrg.jsonAllLayers

class·property 정의 전부. mapAll·mapClass·mapProperty 가 여기서 나온다

tree.jsonld

(읽지 않음)

아래 pruned 를 만드는 재료로만 둔다

tree.pruned.jsonld

CalculatedSchemaOrg.jsonTree

class 계층 트리. getHtmlTree·renderExistingPages 가 걷는다

5.0·14.0·26.0·30.1 넷이 있지만 애플리케이션이 읽는 것은 하나뿐이다. 어느 것인지는 SchemaOrg.scala 의 private val version 이 정한다 — 경로를 여러 곳에 적지 않는다. 2026-09-20 에 26.0 에서 30.1 로 올렸고, 나머지는 과거 것이다.

테스트와 스크립트는 그 상수를 읽는다. scripts/lib/schema-org.mjs 가 SchemaOrg.scala 에서 버전을 뽑아내므로, 한쪽만 올리고 다른 쪽을 잊어도 조용히 지나가지 않는다.

2. 어디서 와서 어떻게 가공되나

원본 둘은 서로 다른 곳에서 온다. 같은 릴리스 페이지에 있지 않다.

  • schemaorg-current-https.jsonld — https://github.com/schemaorg/schemaorg/blob/v<버전>/data/releases/<버전>/
  • tree.jsonld — https://schema.org/docs/tree.jsonld. 릴리스에는 이 파일이 없다. 문서 경로가 내주는 현재 버전이므로, 릴리스 직후가 아니면 어휘와 버전이 어긋날 수 있다

가공은 별도 저장소 SchemaOrgTransform 이 한다. original/<버전>/ 에 원본 둘을 넣고 node SchemaOrgTransform/index.js 를 돌리면 transformed/<버전>/ 에 셋이 나온다.

  • {"@id": "schema:Movie"} 처럼 키가 @id 하나뿐인 객체를 그 값으로 푼다
  • 모든 키와 잎에서 @·rdf:·rdfs:·schema:·http://schema.org/ 접두어를 벗긴다
  • tree.jsonld 에서 id·children 만 남겨 tree.pruned.jsonld 를 만든다

그래서 배포본이 @graph·@id·@type·rdfs:Class·rdf:Property 라고 적는 자리를 우리 파일은 graph·id·type·Class·Property 라고 적는다. SchemaOrg.scala 는 그 가공된 모양을 (v \ "id").as[String] 로 바로 읽는다 — 가공 안 한 파일을 떨어뜨리면 Schema 블록이 있는 첫 페이지에서 JsResultException 이 난다.

test/schema-org-vocabulary.test.mjs 가 그 모양을 이름 붙여 고정한다. 가공을 빠뜨리면 런타임이 아니라 거기서 실패한다.

3. 우리 것이 아닌 용어를 거른다

CalculatedSchemaOrg.isSchemaOrgTerm 이 두 가지를 버린다. 둘 다 26.0 에서는 아무것도 바꾸지 않고 27.0 부터 문다.

  • namespace 가 붙은 id. 27.0 부터 릴리스가 다른 어휘의 용어를 함께 싣는다 — bibo:·cmns-*:·fibo-*:·gs1:·unece:·eli:. 30.1 기준 231개 이고 전부 Class 나 Property 로 잡혀서, 거르지 않으면 클래스 브라우저와 property 추천에 우리 것인 양 섞인다.
  • comment 가 없는 것. 가공이 rdf:·rdfs: 접두어를 벗기는 것은 어쩔 수 없다 — class 가 자기를 class 라고 말하는 방법이 rdfs:Class 라서다. 그 부작용으로 rdf:type 과 rdfs:label 이 맨 type·label 로 도착해 schema.org property 처럼 보이고, 같은 이름의 키와 부딪힌다. 진짜 용어는 모두 rdfs:comment 를 갖고 이 둘만 아무것도 없다 — 그 차이로 가른다.

원본

거른 뒤

mapAll

3256

3023

mapClass

1016

939

mapProperty

1694

1538

3.1. 버린 것을 가리키는 참조도 함께 지운다

용어를 버리고 그것을 가리키는 참조를 남기면 계층이 어긋난 채로 돈다. schema.org class 10개가 외부 class 를 부모로 선언한다 — Brand 는 cmns-cls:Classifier 의 하위이고 Country 는 cmns-ge:GeopoliticalEntity 의 하위다. 그대로 두면 getParents 가 어느 map 에도 없는 이름을 내주고 getPathHierarchy 가 그리로 걸어 들어간다.

그래서 keptOnly 가 subClassOf 와 domainIncludes 에서 namespace 붙은 것을 빼낸다. 10개 모두 schema.org 부모를 함께 갖고 있어서 고아가 되는 class 는 없다. domainIncludes 는 지금 그런 참조가 없지만 같은 규칙으로 덮어 뒀다 — 다음 릴리스가 조용히 하나 넣는 것을 막는다.

getClassHierarchy 는 원래 안전했다. mapClass.get 이 None 이면 Seq() 로 끝나서 외부 이름이 결과에 끼지 않는다. 문제는 그것을 거치지 않는 getParents 쪽이었다.

4. schema.org 에 없는 class 를 직접 정의한다

public/schema.org/custom.jsonld 가 우리가 정의하는 class 를 담는다. 버전 디렉터리 바깥에 있다 — 우리 것이고, 어휘를 올려도 바뀌지 않기 때문이다. 모양은 변환된 어휘와 같아서 parseGraph 가 둘 다 읽는다.

subClassOf 로 진짜 schema.org class 를 가리켜야 한다. 그것이 이 파일의 전부이자 값어치다 — 부모가 있어야 상속 property 추천이 생기고, jsonTree 에 접붙일 자리가 생기고, 그대로가 schema.org 제안서의 내용이 된다.

부모를 안 주면 mapClass 에 없는 class 와 똑같아진다. 그때도 렌더는 된다 — getSchemaClass 가 빈 SchemaType 으로 떨어지므로 — 다만 설명도 부모도 없고, 문서 목록에서 맨 아래 = Custom 평평한 더미로 간다.

4.1. 지금 정의한 것

class

부모

쓰는 곳

제안할 만한가

Standard

CreativeWork

ISO 3166 계열 5개, ISO 8601, ISO 4217, BCP 47, RFC 2119

그렇다

Poem

CreativeWork

꽃, 풀

그렇다

Whiskey

Product

The Macallan Sherry Oak 12 Years Old

아니다

Cognac

Product

Rémy Martin

아니다

넷 다 새 property 가 하나도 필요 없다. 부모만 정해 주면 끝난다.

Standard — 쓰이는 것은 name·url·image·isPartOf·hasPart 뿐이고 전부 CreativeWork 상속분이다. isPartOf·hasPart 가 표준과 그 부분(ISO 3166 ↔ ISO 3166-1)의 관계를 그대로 담는다. Legislation 이 같은 모양의 선례다: CreativeWork 하위의 규범 문서 타입이고, 외부 공동체 어휘(ELI)에서 schema.org 로 들어왔다.

Poem — author·datePublished 뿐이고 둘 다 domain 이 CreativeWork 다. schema.org 에 이미 ShortStory 와 Play 가 있다 — CreativeWork 하위의 문학 형식 타입들이다. 그러니 시가 없는 것은 일부러 거칠게 둔 결과가 아니라 빈자리이고, 그것이 제안이 할 말 전부다.

Whiskey·Cognac — countryOfOrigin 의 domain 에 Product 가 있어서 Product 가 부모다. 제안 대상은 아니다: schema.org 에는 음료 제품 class 가 아예 없다 — Wine 도 Beer 도 FoodProduct 도 없고, Winery 같은 업소 타입과 Intangible 인 MenuItem 뿐이다. 빠진 것이 이름 하나가 아니라 가지 하나라서, 이름 하나를 제안하는 것은 앞뒤가 안 맞는다.

둘은 형제다. 코냑은 브랜디이지 위스키가 아니다. 중간 class(Spirit·AlcoholicBeverage)를 두지 않은 것은 페이지 두 개에 가지를 새로 만드는 일이라서다 — 세 번째 술이 생기면 그때 만든다.

JSON-LD 에는 정의한 이름 그대로 나간다. CreativeWork·Product + additionalType 으로 희석하지 않기로 했다(2026-09-20 소유자 결정) — 주장하려는 것이 «이것은 표준이다», «이것은 시다» 라서다.

4.2. 트리 접붙이기

jsonTree 는 어휘와 다른 파일이라 custom class 를 모른다. seqAll 에만 합치면 Standard 는 맵에서는 부모를 갖는데 정작 문서 목록에서는 = Custom 으로 떨어진다. 그래서 graftChild 가 트리를 읽으면서 부모 노드의 children 에 끼워 넣는다.

4.3. schema.org 가 나중에 같은 이름을 정의하면

seqCustom 이 그 항목을 스스로 버린다. 진짜가 이기고, 우리 것은 쓰이지 않는다. 그리고 테스트가 그 충돌을 이름으로 말해 준다 — 그때가 custom.jsonld 에서 항목을 지우고, 제안을 냈다면 닫을 시점이다. 맵과 트리가 같은 seqCustom 하나를 읽으므로 한쪽에만 남는 일은 없다.

5. 버전 올릴 때

# 1. 원본 둘을 SchemaOrgTransform/original/<새버전>/ 에 놓는다
# 2. index.js 의 버전 목록에 <새버전> 을 더한다  ← 하드코딩이다
# 3. node index.js
# 4. transformed/<새버전>/ 셋을 AhaWiki public/schema.org/<새버전>/ 로 옮긴다(줄끝 LF)
# 5. SchemaOrg.scala 의 `private val version` 을 바꾼다
# 6. npm test 와 sbt test 가 개수를 다시 말해 준다

SchemaOrgTransform/index.js 의 prune 단계는 버전을 하드코딩한다. 목록에 없는 버전은 tree.pruned.jsonld 가 만들어지지 않고, 그 사실을 아무것도 말해 주지 않는다 — 앞 둘만 나오고 조용히 끝난다.

옮길 때 줄끝을 LF 로 맞춘다. SchemaOrgTransform/index.js 는 CRLF 로 쓰고 이 저장소는 LF 로 정규화한다. 2026-09-20 에 transformed/26.0 과 실려 있던 26.0 을 비교하니 바이트는 달랐고 파싱한 내용은 완전히 같았다 — 차이는 줄끝뿐이었다. 그 확인이 곧 이 파이프라인이 실린 파일을 재현한다는 증거다. 30.1 을 더하며 5.0·14.0·26.0 을 다시 돌려 봤고, 셋 다 그대로 재현됐다.

개수는 두 곳이 같이 단언한다 — test/schema-org-vocabulary.test.mjs 는 파일 쪽에서, SchemaOrgUnit.scala 는 적재된 map 쪽에서. 한쪽만 고치면 다른 쪽이 실패한다.

6. 26.0 → 30.1 에서 실제로 달라진 것

  • 사라진 class·property 가 하나도 없다. 위키의 어느 페이지도 쓰던 용어를 잃지 않았다.
  • owner 가 생겼다 — domain=Thing, range=Organization·Person, "A person or organization who owns this Thing." 변환 표가 소유자를 일부러 비워 뒀던 이유가 «schema.org 에 없어서» 였고, 그 자리가 채워졌다. public/js/AhaWiki.WikipediaToSchema.js 의 테스트에 «생기면 다시 보라» 고 걸어 둔 줄이 어휘를 바꾸는 순간 실패해서 알았다. 지금은 Owner·Owners·소유자·소유주·소유기관 이 owner 로 간다.
  • native 신규 property 69개 — pronouns·legalAddress·jobDuration·referee·specification 등.

7. 트리와 어휘가 어긋나는 것

tree.pruned.jsonld 에는 있는데 어휘에는 없는 이름이 StupidType 하나 있다. schema.org 자신의 시험용이고, 문서 트리에는 실리는데 어휘 어느 층에도 정의가 없다. 둘이 서로 다른 곳에서 오기 때문에 생기는 일이다. 테스트가 이 목록을 그대로 고정하므로, 달라지면 실패한다.

26.0 에서는 ProductReturnEnumeration·ProductReturnPolicy 도 같은 처지였다 — attic 으로 물러났는데 트리에 남아 있었다. 30.1 의 트리는 더 이상 그 둘을 부르지 않는다.

의료 전공명들(Dermatology·Nursing 등)은 다르다. 어휘에 type="MedicalSpecialty" 로 있고, 트리에 나오는 것이 맞다 — Class 가 아니라 열거형 값일 뿐이다.

8. See Also

8.1. Similar Pages

Similar pages by cosine similarity. Words after page name are term frequency.

  • Same Wiki
    • 37.82% InterpreterSchema schema(45:21), org(40:6), class(25:3), property(9:7), wiki(2:13), 같은(10:3), map(8:4), json(5:7), aha(2:10), 없는(8:2)
    • 32.79% Dev Testing schema(45:17), 없는(8:8), 같은(10:5), 없다(9:6), 에서(8:6), 것은(7:6), 버전(9:2), test(6:5), 것이(6:4), 다른(6:3)
    • 30.40% Dev Editor schema(45:21), org(40:8), class(25:5), js(8:17), 없다(9:13), aha(2:19), wiki(2:14), interpreter(1:15), 같은(10:5), get(7:7)
  • Sister Wikis
    • 55.92% Aha00a:schema.org schema(45:18), org(40:15), jsonld(12:1), property(9:3), 에서(8:1), json(5:3), type(5:3), 있다(4:2), pages(1:5), person(1:5)

8.2. Adjacent Pages

Control
≤ 32
all
1.0x
1.0x
80
-120
ON
Metrics
Nodes(visible/total)0/0
Links(visible/total)0/0
Avg degree0.00
Depth coverage0
Queue(fetch/graph)0 / 0
Zoom(scale)1.00x
Ctrl/⌘ + Scroll: Zoom
Root 1-hop 2-hop+